﻿using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using StbImageSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Core.Rendering.Resources
{
    public class Texture2D : IDisposable
    {
        public int Id { get; }
        public string? Path { get; }
        public TextureWrapMode WrapModeS { get; set; }
        public TextureWrapMode WrapModeT { get; set; }
        public TextureMagFilter MagFilter { get; set; }
        public TextureMinFilter MinFilter { get; set; }
        public bool IsMinMap { get; set; } = false;
        public bool IsDestroy { get; private set; } = false;
        public static Texture2D Create(string path,
            TextureWrapMode wrapModeS = TextureWrapMode.Repeat,
            TextureWrapMode wrapModeT = TextureWrapMode.Repeat,
            TextureMagFilter magFilter = TextureMagFilter.Linear,
            TextureMinFilter minFilter = TextureMinFilter.Nearest,
            bool isMinMap = false) => new(path, wrapModeS, wrapModeT, magFilter, minFilter, isMinMap);
        public static Texture2D Create(Color4 color)
            => new(color);
        private Texture2D(string path,
            TextureWrapMode wrapModeS = TextureWrapMode.Repeat, TextureWrapMode wrapModeT = TextureWrapMode.Repeat,
            TextureMagFilter magFilter = TextureMagFilter.Linear, TextureMinFilter minFilter = TextureMinFilter.Nearest,
            bool isMinMap = false)
        {
            ImageResult? image = LoadTexture2DFromDisk(path);
            if (image != null)
            {
                Id = GL.GenTexture();
                Path = path;
                GL.ActiveTexture(TextureUnit.Texture0);
                GL.BindTexture(TextureTarget.Texture2D, Id);
                WrapModeS = wrapModeS;
                WrapModeT = wrapModeT;
                MagFilter = magFilter;
                MinFilter = minFilter;
                IsMinMap = isMinMap;
                int ws = (int)wrapModeS;
                int wt = (int)wrapModeT;
                int magf = (int)magFilter;
                int minf = (int)minFilter;
                GL.TextureParameterI(Id, TextureParameterName.TextureWrapS, ref ws);
                GL.TextureParameterI(Id, TextureParameterName.TextureWrapT, ref wt);
                GL.TextureParameterI(Id, TextureParameterName.TextureMagFilter, ref magf);
                GL.TextureParameterI(Id, TextureParameterName.TextureMinFilter, ref minf);
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
                    image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
                if (isMinMap)
                {
                    GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
                }
            }
            GL.BindTexture(TextureTarget.Texture2D, 0);
        }
        private Texture2D(Color4 color, TextureWrapMode wrapModeS = TextureWrapMode.Repeat, TextureWrapMode wrapModeT = TextureWrapMode.Repeat,
            TextureMagFilter magFilter = TextureMagFilter.Linear, TextureMinFilter minFilter = TextureMinFilter.Nearest)
        {
            Id = GL.GenTexture();
            GL.ActiveTexture(TextureUnit.Texture0);
            GL.BindTexture(TextureTarget.Texture2D, Id);
            WrapModeS = wrapModeS;
            WrapModeT = wrapModeT;
            MagFilter = magFilter;
            MinFilter = minFilter;
            int ws = (int)wrapModeS;
            int wt = (int)wrapModeT;
            int magf = (int)magFilter;
            int minf = (int)minFilter;
            GL.TextureParameterI(Id, TextureParameterName.TextureWrapS, ref ws);
            GL.TextureParameterI(Id, TextureParameterName.TextureWrapT, ref wt);
            GL.TextureParameterI(Id, TextureParameterName.TextureMagFilter, ref magf);
            GL.TextureParameterI(Id, TextureParameterName.TextureMinFilter, ref minf);
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f,
                1, 1, 0, PixelFormat.Rgba, PixelType.Float, new[] { color.R, color.G, color.B, color.A });
            GL.BindTexture(TextureTarget.Texture2D, 0);
        }
        private ImageResult? LoadTexture2DFromDisk(string path)
        {
            try
            {
                //StbImage.stbi_set_flip_vertically_on_load(1);
                return ImageResult.FromStream(File.OpenRead(path), ColorComponents.RedGreenBlueAlpha);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }
        public void Bind(int solt = 0)
        {
            TextureUnit unit = TextureUnit.Texture0 + solt;
            GL.ActiveTexture(unit);
            GL.BindTexture(TextureTarget.Texture2D, Id);
        }
        public static void UnBind()
        {
            GL.BindTexture(TextureTarget.Texture2D, 0);
        }
        private void ReleaseUnmanageResources()
        {
            //GL.DeleteTexture(Id);
        }
        public void Dispose()
        {
            if (IsDestroy) return;
            IsDestroy = true;
            ReleaseUnmanageResources();
            GC.SuppressFinalize(this);
        }
        ~Texture2D()
        {
            ReleaseUnmanageResources();
        }
    }
}
